#pragma once

#include <iostream>
#include <pthread.h>
#include <vector>
#include <semaphore.h>
template <class T>
class ring_queue
{
  #define _MAX_CAP 10
private:
  std::vector<T> _v;
  int _cap = _MAX_CAP;

  int _c_step;
  int _p_step;

  sem_t _blank_sem;
  sem_t _data_sem;

  pthread_mutex_t _c_mutex;
  pthread_mutex_t _p_mutex;

public:
  ring_queue(int cap = _MAX_CAP)
  {
    _v.reserve(_cap);
    sem_init(&_blank_sem,0,_cap);
    sem_init(&_data_sem,0,0);
    pthread_mutex_init(&_c_mutex,nullptr);
    pthread_mutex_init(&_p_mutex,nullptr);
    _c_step = _p_step = 0;
  }
  ~ring_queue()
  {
    sem_destroy(&_blank_sem);
    sem_destroy(&_data_sem);
    pthread_mutex_destroy(&_c_mutex);
    pthread_mutex_destroy(&_p_mutex);
  }
  void push(T& data)
  {
    std::cout << "push:" << pthread_self() << ": "; 
    sem_wait(&_blank_sem);
    pthread_mutex_lock(&_p_mutex);
    _v[_p_step] = data;
    ++_p_step;
    _p_step %= _cap;
    pthread_mutex_unlock(&_p_mutex);
    sem_post(&_data_sem);
  }
  T pop(T& data)
  {
    std::cout<< "pop:" << pthread_self() << ": "; 
    sem_wait(&_data_sem);
    pthread_mutex_lock(&_c_mutex);
    data = _v[_c_step];
    ++_c_step;
    _c_step %= _cap;
    pthread_mutex_unlock(&_c_mutex);
    sem_post(&_blank_sem);
    return data;
  }
};
